#include "CHardwareFacade.h"
#include "sleeperthread.h"
#include <stdio.h>
#include <string.h>

//_________________________________________________________________________
void CHardwareFacade::StringToLower ( char *str, int length )
{
    int i;

    if ( str==NULL)
        return ;

    if (length == -1)
        length = strlen (str);

    for (i=0; i<length; i++)
        str[i]=tolower(str[i]);
}

//_________________________________________________________________________
unsigned short CHardwareFacade::crc16_update(unsigned short crc, unsigned char a)
{
    int i;

    crc ^= a;
    for (i = 0; i < 8; ++i)
    {
        if (crc & 1)
            crc = (crc >> 1) ^ 0xA001;
        else
            crc = (crc >> 1);
    }

    return crc;
}

//_________________________________________________________________________
int CHardwareFacade::LocateSubString( char* main, char* sub, int lengthMain, int lengthSub)
{
    int i;

    for (i=0; i<lengthMain-lengthSub; i++)
    {
        if ( strncmp(main+i, sub, lengthSub) == 0 )
            return i;
    }

    return -1;
}

//_________________________________________________________________________
int CHardwareFacade::CheckConnectionAndType (  )
{
    int readLength;
    char dataTemp[1024] = {0};
    m_FoundSystemType = FOUND_NOTHING;     // 0: FOUND_NOTHING; 1: FOUND_MOUSSAVI; 2: FOUND_ANIBIKE
    m_EchoIsOn = ECHO_UNKNOWN;

    // if simulating - make it ANIBIKE as default and return
    if (m_Simulation)
    {
        m_FoundSystemType = FOUND_ANIBIKE;
        m_ParametersValid = false;
        m_FSValid = false;
        GetParameters();
        return m_FoundSystemType;      // simulation of this particular device
    }

    // check which of the known devices this port is hiding
    if (m_info.friendName.contains (MOUSSAVI_PRODUCT_ID) )
    {
        m_Simulation = 0;
        m_FoundSystemType = FOUND_MOUSSAVI;

        // close the serial port before making any change
        if (serialPort->isOpen())
        {
            serialPort->close();
            msleep(100);
        }

        //serialPort->setBaudRate(BAUD9600);
        m_ParametersValid = false;
        m_FSValid = false;
        m_FrameData = NULL;
        m_CurrDataLength = 0;
    }
    else if ((m_info.enumName.contains ( "BTHENUM" ) && m_info.hardwareID.endsWith("LOCALMFG&000f")) ||
             (m_info.enumName.contains ( "dev" ) && (m_info.portName.contains ( "rfcomm" ))) )
    {
        int portOpen = 0;
        m_Simulation = 0;

        // close the serial port before making any change
        if (serialPort->isOpen())
        {
            serialPort->close();
            msleep(100);
        }

        //serialPort->setBaudRate(BAUD9600);
        //msleep(1000);
        portOpen = serialPort->open (QIODevice::ReadWrite);

        // if we actually succeeded openning that port
        if (portOpen)
        {
            serialPort->write(":");
            msleep(100);
            serialPort->write(":");
            msleep(100);
            serialPort->write(":");
            msleep(100);
            serialPort->write(":");
            msleep(100);
            serialPort->write("eof:");
            msleep(200);
            serialPort->write("eeooff:");
            msleep(200);
            serialPort->write("idn?:");
            msleep(200);
            serialPort->write("idn?:");
            msleep(200);
            WAIT_FOR_BYTES_N(150,20);
            readLength = serialPort->read (dataTemp, 1024);

            if (readLength && strstr(dataTemp, ANIBIKE_PRODUCT_ID)!=NULL)
            {
                m_FoundSystemType = FOUND_ANIBIKE;

                m_ParametersValid = false;
                m_FSValid = false;
                m_FrameData = NULL;
                m_CurrDataLength = 0;
            }

            // close the open port anyway
            serialPort->close();
        }
    }

    // as a default return that its not a known device
    return m_FoundSystemType;
}

//_________________________________________________________________________
CHardwareFacade::CHardwareFacade( QObject* parent):
    QThread(parent),m_Simulation(0), m_Connected(0)
{
    m_FrameData = NULL;

    m_abortJob = false;
    m_exitWorkerThread = false;
    m_working = false;
    m_deviceNumber = -1;
}

//_________________________________________________________________________
void CHardwareFacade::SetInitParameters (QextPortInfo info, bool simulation)
{
    m_Simulation = simulation;
    m_info = info;

    if (!m_Simulation)
    {
        serialPort = new QextSerialPort(m_info.portName);
        if (serialPort->isOpen())
        {
            serialPort->close();
            msleep(100);
        }

        serialPort->setPortName(m_info.portName);
        serialPort->setDtr (false);
        serialPort->setRts (false);
        serialPort->setBaudRate(BAUD9600);
        serialPort->setFlowControl(FLOW_OFF);
        serialPort->setParity(PAR_NONE);
        serialPort->setDataBits(DATA_8);
        serialPort->setStopBits(STOP_1);
        serialPort->setTimeout(500);
    }
}

//_________________________________________________________________________
/*CHardwareFacade::CHardwareFacade(QextPortInfo info, bool simulation)
    :m_Simulation(simulation), m_Connected(0)
{
    m_info = info;
    m_FrameData = NULL;

    if (!m_Simulation)
    {
        serialPort = new QextSerialPort(m_info.portName);
        if (serialPort->isOpen())
        {
            serialPort->close();
            msleep(100);
        }

        serialPort->setPortName(m_info.portName);
        serialPort->setDtr (false);
        serialPort->setRts (false);
        serialPort->setBaudRate(BAUD9600);
        serialPort->setFlowControl(FLOW_OFF);
        serialPort->setParity(PAR_NONE);
        serialPort->setDataBits(DATA_8);
        serialPort->setStopBits(STOP_1);
        serialPort->setTimeout(500);
    }

    m_abortJob = false;
    m_exitWorkerThread = false;
}*/

//_________________________________________________________________________
CHardwareFacade::CHardwareFacade(const CHardwareFacade& hardware)
{
    memcpy (&m_HWParams, &hardware.m_HWParams, sizeof (CHardwareParams));
    m_Simulation = hardware.m_Simulation;
    m_ParametersValid = hardware.m_ParametersValid;
    m_FSValid = hardware.m_FSValid;
    m_Connected = hardware.m_Connected;
    m_EchoIsOn = hardware.m_EchoIsOn;

    if ( hardware.m_FrameData != NULL )
    {
        m_FrameData = new unsigned char [m_HWParams.FlashBlockSizeBytes*m_HWParams.NumOfBlocksForFrame*2];
        memcpy(m_FrameData, hardware.m_FrameData, m_HWParams.FlashBlockSizeBytes*m_HWParams.NumOfBlocksForFrame*2);
    }

    m_CurrDataLength = hardware.m_CurrDataLength;

    m_FoundSystemType = hardware.m_FoundSystemType;
    m_info = hardware.m_info;

    // copy the pointer to the pre allocated serial-port
    serialPort = hardware.serialPort;

    m_abortJob = false;
    m_exitWorkerThread = false;
    m_working = false;
    m_deviceNumber = hardware.m_deviceNumber;
}

//_________________________________________________________________________
CHardwareFacade::~CHardwareFacade(void)
{
    DisconnectDevice();

    if (m_Simulation)
    {

    }
    else
    {
        // do not release the serial-port
    }

    // terminate the current job
    // TBD?!?!?!?!?
    /*m_mutex.lock();
    m_abortJob = true;
    m_exitWorkerThread = true;
    m_condition.wakeOne();
    m_mutex.unlock();

    // wait for the termination
    wait();*/

    // delete stuff
    delete []m_FrameData;
    m_FrameData = NULL;
    m_CurrDataLength = 0;
}

//_________________________________________________________________________
void CHardwareFacade::DeleteSerialPort ( void )
{
    if (serialPort!=NULL)
        delete serialPort;
    serialPort = NULL;
}

//_________________________________________________________________________
void CHardwareFacade::setDeviceNumber (int number)
{
    m_deviceNumber = number;
}

//_________________________________________________________________________
int CHardwareFacade::ConnectDevice()
{
    if (m_Simulation)
    {
        m_Connected = true;
        return 0;
    }
    else
    {
        msleep (400);
        m_Connected = serialPort->open (QIODevice::ReadWrite);

        if (!m_Connected)
        {
            //m_Simulation = true;
            //GetParameters();
            return -1;
        }
        EchoOffCommand ( );
    }

    return 0;
}

//_________________________________________________________________________
void CHardwareFacade::DisconnectDevice(void)
{
    if (m_Simulation)
    {
        m_Connected = false;
        m_ParametersValid = false;
        m_FSValid = false;

        return;
    }
    else
    {
        //CommandAndCheck ( "sw_reset:" );
//        EchoOnCommand ( );
        if (serialPort!=NULL)
        {
            if (serialPort->isOpen())
                serialPort->close();
        }
        m_Connected = false;
        m_ParametersValid = false;
        m_FSValid = false;
    }

    delete []m_FrameData;
    m_FrameData = NULL;
    m_CurrDataLength = 0;
}

//_________________________________________________________________________
void CHardwareFacade::ResetDevice (void)
{
    serialPort->write ( "sw_reset:" );
}

//_________________________________________________________________________
bool CHardwareFacade::IsConnected(void)
{
    if (m_Simulation)
    {

    }
    else
    {
        m_Connected = serialPort->isOpen ();
    }
    return m_Connected;
}

//_________________________________________________________________________
int CHardwareFacade::SendMessageAndWait ( QString msg, QString eol, QString& retVal, int timeoutMS )
{
    char dataTemp[1024] = {0};
    char sentCmd[1024] = {0};
    char sentCmd1[1024] = {0};
    int readLength;
    int i;
    int timeCount = timeoutMS;
    int placeCount = 0;
    int eol_found = 0;

    if (m_Simulation)
    {
        retVal.clear();
        qDebug("In simulation mode.\n");
        return -1;
    }


    if (!serialPort->isOpen ())
    {
        retVal.clear();
        qDebug("Serial-port not open.\n");
        return -1;
    }

    qDebug("Sending Message: \n%s\n", msg.toAscii().data());

    // create the actual messages
    if (m_EchoIsOn == ECHO_ON)
    {
        // duplicate all letters
        for (i=0; i<msg.length()-1; i++)
        {
            sentCmd[2*i]=msg.toAscii()[i];
            sentCmd[2*i+1]=msg.toAscii()[i];
        }
        sentCmd[2*(msg.length()-1)]=':';
    }
    else if (m_EchoIsOn == ECHO_OFF)
    {
        // duplicate all letters
        for (i=0; i<msg.length(); i++)
        {
            sentCmd[i]=msg.toAscii()[i];
        }
    }
    else if (m_EchoIsOn == ECHO_UNKNOWN)
    {
        // duplicate all letters
        for (i=0; i<msg.length()-1; i++)
        {
            sentCmd[2*i]=msg.toAscii()[i];
            sentCmd[2*i+1]=msg.toAscii()[i];
        }
        sentCmd[2*(msg.length()-1)]=':';

        // duplicate all letters
        for (i=0; i<msg.length(); i++)
        {
            sentCmd1[i]=msg.toAscii()[i];
        }
    }

    qDebug("Real Message: \n%s\n", sentCmd);


    // flush all data in the TX and RX
    readLength = serialPort->read (dataTemp, 1024);

    // send the actual messages
    msleep (200);
    serialPort->write(sentCmd);
    msleep (200);

    if (m_EchoIsOn == ECHO_UNKNOWN)
    {
        msleep (200);
        serialPort->write(sentCmd1);
    }

    // wait for the eol to income
    eol_found = 0;
    while (!eol_found)
    {
        timeCount = timeoutMS;
        while ( timeCount-- && !serialPort->bytesAvailable () ) { msleep (1); if (timeCount<0) break;}

        readLength = serialPort->read (dataTemp+placeCount, 1024);
        placeCount += readLength;

        if (strstr( dataTemp, eol.toAscii().data() )!=NULL)     // a match was found
        {
            eol_found = 1;
            break;
        }
        if (!timeCount) break;
    }

    if (eol_found)
    {
         retVal = QString(dataTemp);

         qDebug("Recieved Message: \n%s\nSize: %d\nElapsed: %d\n", dataTemp, placeCount, timeoutMS-timeCount);
    }
    else
    {
        retVal.clear();
        qDebug("Didn't get anything back!\n");
    }

    return 0;
}

//_________________________________________________________________________
int CHardwareFacade::GetIdentificationString ( QString& retVal, int timeoutMS )
{
    return SendMessageAndWait ( "idn?:", "eol", retVal, timeoutMS );
}

//_________________________________________________________________________
int CHardwareFacade::GetParametersString ( QString& retVal, int timeoutMS  )
{
    return SendMessageAndWait ( "repa:", "eol", retVal, timeoutMS  );
}

//_________________________________________________________________________
int CHardwareFacade::StartAllCommand ( int timeoutMS )
{
    QString retVal;
    return SendMessageAndWait ( "start_all:", "OK", retVal, timeoutMS );
}

//_________________________________________________________________________
int CHardwareFacade::StopAllCommand ( int timeoutMS )
{
    QString retVal;
    return SendMessageAndWait ( "stop_all:", "OK", retVal, timeoutMS );
}

//_________________________________________________________________________
int CHardwareFacade::EchoOnCommand ( int timeoutMS )
{
    QString retVal;
    if (SendMessageAndWait ( "eon:", "OK", retVal, timeoutMS )==0)
    {
        m_EchoIsOn=ECHO_ON;
        return 0;
    }
    return -1;
}

//_________________________________________________________________________
int CHardwareFacade::EchoOffCommand ( int timeoutMS )
{
    QString retVal;
    if (SendMessageAndWait ( "eof:", "OK", retVal, timeoutMS )==0)
    {
        m_EchoIsOn=ECHO_OFF;
        return 0;
    }
    return -1;
}

//_________________________________________________________________________
CHardwareParams*  CHardwareFacade::GetParameters(void)
{
    int problemOccured = 0;
    int result = 0;
    QString idnRet;
    QString repaRet;

    if (m_ParametersValid)
    {
        return (&m_HWParams);
    }

    if (!m_Simulation)
    {
        if (!serialPort->isOpen ())
        {
            qDebug("GetParameters: Serial-port not open.\n");
            return NULL;
        }
    }

    if (!m_Simulation)
    {
        char data[1024];
        char *token;

        // in bluetooth communication it wakes up the heavy sleep mode
        result = GetIdentificationString ( idnRet, 2000 );

        if (result != 0)
        {
            problemOccured = 1; // idn? command didn't respond
            return NULL;
        }
        else
        {
            /*"MOUSSAVI VERSION:%03d.%d;"	// The project
            "MEM_SIZE:%05d MBITS;"		// Memory Size
            "CLOCK:%03d MHZ;"			// CPU SIZE
            "#LEDS:%03d;"			// number of leds
            "RGB:%d;" 				// RGB?
            "MEMBLK:%02d KB"            */

            strcpy(data, idnRet.toAscii().data());

            if ( strstr(data, MOUSSAVI_PRODUCT_ID) != NULL ||
                 strstr(data, ANIBIKE_PRODUCT_ID) != NULL ) // is it moussavi or anibike system
            {
                if (strstr(data, MOUSSAVI_PRODUCT_ID) != NULL)
                {
                    strcpy(m_HWParams.m_Product, MOUSSAVI_PRODUCT_ID);
                }
                else if (strstr(data, ANIBIKE_PRODUCT_ID) != NULL)
                {
                    strcpy(m_HWParams.m_Product, ANIBIKE_PRODUCT_ID);
                }

                strcpy(m_HWParams.m_Vendor, VENDOR_ID);

                //token = strtok(data, ":");
                token = strtok(data, ":");
                token = strtok(NULL, ".");
                m_HWParams.Version = atoi(token);
                token = strtok(NULL, ";\r");
                m_HWParams.SubVersion = atoi(token);

                token = strtok(NULL, ":");
                token = strtok(NULL, ";\r");
                m_HWParams.MemorySize = atoi(token);

                token = strtok(NULL, ":");
                token = strtok(NULL, ";\r");
                m_HWParams.CPUClock = atoi(token);

                token = strtok(NULL, ":");
                token = strtok(NULL, ";\r");
                m_HWParams.NumberOfLEDs = atoi(token);

                token = strtok(NULL, ":");
                token = strtok(NULL, ";\r");
                m_HWParams.RGBAvailable = atoi(token);

                token = strtok(NULL, ":");
                token = strtok(NULL, ";\r");
                m_HWParams.FlashBlockSizeBytes = atoi(token);
            }
        }

        if (!problemOccured)
        {
            result = GetParametersString ( repaRet, 500 );

            if (result != 0)
            {
                problemOccured = 1; // idn? command didn't respond
                return NULL;
            }
            else
            {
                /*"COLOR_DEPTH:%03d;"
                "FILE#BLKS:%02d;"
                "FN_LEN:%03d;"
                "MAX_FRAMES:%03d;\r\n"      */

                strcpy(data, repaRet.toAscii().data());

                token = strtok(data, ":");
                token = strtok(NULL, ";\r");
                m_HWParams.ColorDepth = atoi (token);

                token = strtok(NULL, ":");
                token = strtok(NULL, ";\r");
                m_HWParams.NumOfBlocksForFrame = atoi (token);

                token = strtok(NULL, ":");
                token = strtok(NULL, ";\r");
                m_HWParams.FilenameLength = atoi (token);

                token = strtok(NULL, ":");
                token = strtok(NULL, ";\r");
                m_HWParams.FileMaxNumberOfFrames = atoi (token);

                if (m_FrameData!=NULL)
                {
                    delete []m_FrameData;
                    m_FrameData = NULL;
                }

                m_FrameData = new unsigned char[m_HWParams.FlashBlockSizeBytes*
                                                m_HWParams.NumOfBlocksForFrame*2];
                m_CurrDataLength = 0;
                m_ParametersValid = true;

            }
        }
    }

    if (m_Simulation)
    {
        m_Simulation = 1;
        m_HWParams.ColorDepth=2;
        m_HWParams.CPUClock=16;
        m_HWParams.MemorySize=32;
        strcpy(m_HWParams.m_Product, ANIBIKE_PRODUCT_ID);
        strcpy(m_HWParams.m_Vendor, VENDOR_ID);
        m_HWParams.NumberOfLEDs=64;
        m_HWParams.RGBAvailable=1;
        m_HWParams.SubVersion=7;
        m_HWParams.Version=1;
        m_HWParams.FlashBlockSizeBytes=4096;
        m_HWParams.NumOfBlocksForFrame=3;
        m_HWParams.FilenameLength=12;
        m_HWParams.FileMaxNumberOfFrames = 1;

        m_ParametersValid = true;
    }
    return (&m_HWParams);
}

//_________________________________________________________________________
int CHardwareFacade::GetFileSystemRaw(unsigned char* data, int *length)
{
    char dataTemp[2048];
    int count = 0;
    int errorOccured = 0;
    int readLength = 0;
    int timeCount = 10000;
    int timeoutMS = 10000;
    int eodPlace = 0;

    if (m_Simulation)
    {
        return 2;   // empty
    }


    if (!serialPort->isOpen ())
    {
        qDebug("GetFileSystemRaw: Serial-port not open.\n");
        return -1;
    }

//    EchoOffCommand();

/*    if (m_FrameData == NULL)
    {
        m_FrameData = new unsigned char[m_HWParams.FlashBlockSizeBytes*
                                        m_HWParams.NumOfBlocksForFrame*2];
    }
*/
    if (m_EchoIsOn==ECHO_ON)
    {
        serialPort->write ("ggeett__ffss:");
        qDebug("Echo is on, posting: \nggeett__ffss:\n");
    }
    else
    {
        serialPort->write ("get_fs:");
        qDebug("Echo is off, posting: \nget_fs:\n");
    }

    int eod_found = 0;
    while (!eod_found)
    {
        while ( timeCount-- && !serialPort->bytesAvailable () ) { msleep (1); }

        readLength = serialPort->read (dataTemp, 1024);
        memcpy ((void*)(data+count), (void*)(dataTemp), readLength);
        count += readLength;

        eodPlace = LocateSubString((char*)(data), (char*)("FINISHED.FINISHED.FINISHED."), count, strlen("FINISHED.FINISHED.FINISHED."));
        if (eodPlace!=-1)     // a match was found
        {
            qDebug("Got information:\nLength: %d\nElapsed: %d", count, timeoutMS-timeCount);
            data[eodPlace]=0xff;
            data[eodPlace+1]=0xff;
            eod_found = 1;          // got the information
            break;
        }

        eodPlace = LocateSubString((char*)(data), (char*)("EMPTY.EMPTY.EMPTY."), count, strlen("EMPTY.EMPTY.EMPTY."));
        if (eodPlace!=-1)     // a match was found
        {
            qDebug("The file system appears to be empty.\nElapsed: %d", timeoutMS-timeCount);
            data[eodPlace]=0xff;
            data[eodPlace+1]=0xff;
            eod_found = 2;          // the file system is empty
            break;
        }

        if (!timeCount)
        {
            errorOccured = 1;
            break;
        }
    }

    // check if everything was ok
    if (errorOccured)
    {
        qDebug("Timeout error while waiting for RAW FS.\n");
        return -1;
    }

    if (eod_found == 0)
    {
        qDebug("Didn't get anything back!\n");
        return -1;
    }

    //memcpy (data, m_FrameData, eodPlace);
 //   *data = m_FrameData ;
    *length = eodPlace;

    //EchoOnCommand();

    return eod_found;
}

//_________________________________________________________________________
int CHardwareFacade::TransferSingleBlock( int blockNum,
                                          unsigned char *data,
                                          int length,
                                          bool erase_unprotect,
                                          QProgressBar* progress )
{
    char transData[1024];
    char dataTemp[1024];
    int readLength;
    int writingChunk = 15;      // this is the atommic writing ammount
    int bytesToWrite = length;
    int counter, currOffset = 0;
    char *token;
    int resStart, resEnd;
    double progValue;


    if (m_Simulation)
    {
        // as if all the data has been transferred
        return 0;
    }

    if (bytesToWrite>m_HWParams.FlashBlockSizeBytes)
        bytesToWrite = m_HWParams.FlashBlockSizeBytes;

    counter = bytesToWrite;

    if (!serialPort->isOpen ())
    {
        qDebug("TransferSingleBlock: Serial-port not open.\n");
        return -1;
    }

//    EchoOffCommand();

    if (erase_unprotect)
    {
        sprintf(transData, "erase_flash %d:", blockNum);
        CommandAndCheck (transData);

        sprintf(transData, "unprot_flash %d:", blockNum);
        CommandAndCheck (transData);
    }

    currOffset = m_HWParams.FlashBlockSizeBytes * blockNum;        // this is the basic offset address

    // PROGRESS
    if (progress)
    {
        progValue = 0;
        progress->setValue (0);
    }
    // PROGRESS

    while (counter)
    {
        int currentChunk = (writingChunk<counter)?writingChunk:counter;

        sprintf(transData, "write %d %d:", currOffset, currentChunk);
        serialPort->write (transData, strlen(transData));
        WAIT_FOR_BYTES_N(1000, 16)             // wait for response
        readLength = serialPort->read (dataTemp, 1024);
        dataTemp[readLength] = '\0';

        // understand whats written in the "writing: %lu to %lu;" line
        token = strtok (dataTemp, " ");
        if (strcmp (token, "writing:") != 0)
            return -1;

        token = strtok (NULL, " ");
        resStart = atoi (token);
        token = strtok (NULL, " ");
        token = strtok (NULL, ";\r");
        resEnd = atoi (token);

        // if there is a need to shorten the packets, do it
        if ( (resEnd-resStart) < currentChunk )
            currentChunk = resEnd-resStart;


        // Send the actual data
        serialPort->write ((const char*)(data+currOffset-m_HWParams.FlashBlockSizeBytes*blockNum),
                           currentChunk);

        // wait for the "finished"
        WAIT_FOR_BYTES_N(1000, 7)             // wait for response
        readLength = serialPort->read (dataTemp, 1024);
        dataTemp[readLength] = '\0';

        // Didn't get any response
        if ( strstr(dataTemp, "finished") == NULL )
        {
            return -2;
        }


        currOffset += currentChunk;
        counter -= currentChunk;

        // PROGRESS
        if (progress)
        {
            progValue = (double)(currOffset-m_HWParams.FlashBlockSizeBytes*blockNum)/
                        (double)(bytesToWrite)*100.0;
            progress->setValue ((int)(progValue));
        }
        // PROGRESS
    }

    sprintf(transData, "prot_flash %d:", blockNum);
    CommandAndCheck (transData);

 //   EchoOnCommand();

    return length-bytesToWrite;
}

//_________________________________________________________________________
int CHardwareFacade::TransferSingleBlockCRC16( int blockNum,
                              unsigned char *data,
                              int length,
                              int totalLength,
                              bool erase_unprotect,
                              bool post_protect,
                              CFileSystem *fs )
{
    char cmd[256] = {0};
    int transactionResult = 0;
    unsigned char *transData = data;
    int counter, currentLength;
    int quant;
    int success = 0;
    double progValue;
    int currentQuantNum = 0, currentQuantLength = 0;
    int maxTrialsQuant = 5;
    int tries = 5;

    if (m_Simulation)
    {
        // as if all data was transferred successfully
        return 0;
    }

    if (!serialPort->isOpen ())
    {
        qDebug("TransferSingleBlockCRC16: Serial-port not open.\n");
        return -1;
    }

    if ( !transData ) return -2;

    // The file bytes transfer quantization calculation
    if ( !strcmp(m_HWParams.m_Product, ANIBIKE_PRODUCT_ID) )        // for ANIBIKE system
    {
        quant = 64;
    }
    else if ( !strcmp(m_HWParams.m_Product, MOUSSAVI_PRODUCT_ID) )        // for MOUSSAVI system
    {
        quant = 15;
    }

    // set the counter to be minimum between length and flash block size
    currentLength = counter = (length > m_HWParams.FlashBlockSizeBytes)?m_HWParams.FlashBlockSizeBytes:length;

    if (erase_unprotect)
    {
        sprintf(cmd, "erase_flash %d:", blockNum);
        CommandAndCheck (cmd);

        sprintf(cmd, "unprot_flash %d:", blockNum);
        CommandAndCheck (cmd);
    }


    // PROGRESS
    progValue = (double)(totalLength-length)/(double)(totalLength)*100.0;
    if (progValue<0.0)  progValue = 0.0;
    if (progValue>100.0) progValue = 100.0;
    requestUpdateProgressBar(m_deviceNumber, (int)(progValue));
    // PROGRESS

    sprintf(cmd, "write_blk %d %d %d:", blockNum, quant, currentLength);
    tries = 5;
    while ( CommandAndCheck ( cmd ) != 0 && tries-- ) { }
    if (!tries)
    {
        // the other side didn't get the command after lots of tries
//        EchoOnCommand();
        return -3;
    }

    msleep(10);

    currentQuantNum = 0;
    maxTrialsQuant = 5;
    while (counter && maxTrialsQuant)
    {
        // point to the start of the new transaction
        transData = &data[currentQuantNum*quant];

        // calculate the current transaction length
        currentQuantLength = ((counter)>quant)?quant:(counter);

        // try transferring the information with one last WORD which is the crc16 value
        // at total there will be quant+2 bytes (anibike - 34)
        success = 0;
        tries = 5;
        do {

            transactionResult = SendDataCRC16 ( transData, currentQuantLength );

            if (transactionResult != 0) // error
            {
                // can happen in the following conditions
                //  1. the crc16 was wrong
                //  2. some bytes were lost in the transaction
                //  3. the other side doesn't exist

                if (transactionResult==1)       // crc fail - just make the same transaction again
                {
                    success = 0;
                    continue;
                }
                else if (transactionResult==-2)      // timed out waiting for the response
                {
                    int maxTrials = quant;
                    success = 0;

                    // maybe not all the bytes were sent so the other side waits for the last data.
                    // we try to send byte per byte and try to get the "fail". otherwise there is
                    // no communication. the maximal trials will be quant as its the maximal number
                    // of bytes that could possibly dissappear.
                    while ( CommandAndCheck(":")==-2 && maxTrials--)  {}

                    if (maxTrials==0)       // the other side doen't respond/exists
                    {
                        return -5;
                    }
                }
                else if (transactionResult==-1)      // the other side doesn't exits
                {
                    return -5;
                }
            }
            else
            {
                success = 1;
            }
        } while (success != 1 && tries--);

        // update the counter value
        if (success)
        {
            counter -= currentQuantLength;
            currentQuantNum ++;
            maxTrialsQuant = 5;

            // PROGRESS
            progValue = (double)(totalLength-length+(currentLength-counter))/(double)(totalLength)*100.0;
            if (progValue<0.0)  progValue = 0.0;
            if (progValue>100.0) progValue = 100.0;
            requestUpdateProgressBar(m_deviceNumber, (int)(progValue));
            // PROGRESS
        }
        else
        {
            maxTrialsQuant --;
        }
    }

    if (maxTrialsQuant==0)      // the block was not written at all/completely
    {
        // erase the appropriate block and return error
        //sprintf(cmd, "erase_flash %d:", blockNum);
        //CommandAndCheck (cmd);
    }

    // protect the written block
    if (post_protect)
    {
        sprintf(cmd, "prot_flash %d:", blockNum);
        CommandAndCheck (cmd);
    }

    return length-currentLength;
}

//_________________________________________________________________________
int CHardwareFacade::ReadOutSingleBlock( int blockNum,
                                         unsigned char *data,
                                         QProgressBar* progress )
{
    char cmd[256] = {0};
    char *transData = NULL;
    int readLength;
    int counter;
    double progValue;

    if (m_Simulation)
    {
        // fill all the data with '0'
        memset (data, 0, m_HWParams.FlashBlockSizeBytes);
        return 0;
    }

    if (!serialPort->isOpen ())
    {
        qDebug("ReadOutSingleBlock: Serial-port not open.\n");
        return -1;
    }

    transData = new char[2*m_HWParams.FlashBlockSizeBytes];
    if (!transData)     // couldn't allocate memory
    {
        return -3;
    }

    counter = m_HWParams.FlashBlockSizeBytes;

//    EchoOffCommand();

    //currOffset = m_HWParams.FlashBlockSizeBytes * blockNum;        // this is the basic offset address

    // PROGRESS
    if (progress)
    {
        progValue = 0;
        progress->setValue (0);
    }
    // PROGRESS

    while (counter)
    {
        sprintf(cmd, "read_blk %d:", blockNum);
        serialPort->write (cmd, strlen(cmd));

        WAIT_FOR_BYTES_N(1000,counter)  // wait for response
        readLength = serialPort->read (transData, counter);
        transData[readLength] = '\0';

        counter -= readLength;
        if ( counter < 0) counter = 0;

        // PROGRESS
        if (progress)
        {
            progValue = (1.0-(double)(counter)/(double)(m_HWParams.FlashBlockSizeBytes))*100.0;
            progress->setValue ((int)(progValue));
        }
        // PROGRESS
    }

    memcpy (data, transData, m_HWParams.FlashBlockSizeBytes);

//    EchoOnCommand();

    delete []transData;

    return counter;
}

//_________________________________________________________________________
int CHardwareFacade::TransferFile (CFileSystem *fs, file_list_it_t& file)
{
    int j,count, iTemp;
    int numFrames = file->GetNumOfFrames();

    int length;
    unsigned char* data;

    for (j = 0; j<numFrames; j++)
    {
        int numBlock = file->m_BlockList[j];
        if (fs->GetMemVec(numBlock) == FILE_SYSTEM_MEMVAC_OVERWRITTEN)
        {
            file->GetFrameData(j, &length, &data);

            count = 0;
            iTemp = length;
            while ( iTemp > 0 )
            {
                iTemp = TransferSingleBlockCRC16 ( numBlock*m_HWParams.NumOfBlocksForFrame + count,
                                                   data + count*m_HWParams.FlashBlockSizeBytes,
                                                   length - count*m_HWParams.FlashBlockSizeBytes,
                                                   (numFrames-j)*m_HWParams.NumOfBlocksForFrame*m_HWParams.FlashBlockSizeBytes,
                                                   true, true, fs);

                count ++;

            }
        }
    }

    file->IntermediateValidateTable();
    return 0;
}

//_________________________________________________________________________
int CHardwareFacade::TransferEntryTable (CFileSystem *fs)
{
    int count = 0, iTemp = 0;
    int entryTableAddress = fs->GetEntryTableStartAddress ();

    unsigned char *entryTableData;
    int entryTableLength;
    fs->GetEntryTableData (&entryTableLength, &entryTableData);

    // DEBUGING
    int entryLength = m_HWParams.GetEntrySize();
    for (int i=0; i<entryTableLength/entryLength; i++)
    {
        char tempLine[1024] = {0};
        for (int j=0; j<entryLength; j++)
        {
            sprintf(tempLine, "%s 0x%x", tempLine, entryTableData[entryLength*i+j]);
        }

        qDebug ("Entry number %d:", i);
        qDebug ("%s\n", tempLine);
    }
    // /DEBUGING

    iTemp = entryTableLength;

    while ( iTemp > 0 )
    {
        iTemp = TransferSingleBlockCRC16 ( entryTableAddress/m_HWParams.FlashBlockSizeBytes + count,
                                           entryTableData + count*m_HWParams.FlashBlockSizeBytes,
                                           entryTableLength - count*m_HWParams.FlashBlockSizeBytes,
                                           entryTableLength,
                                           true, true,
                                           fs);
        count ++;
    }

    delete []entryTableData;

    return 0;
}

//_________________________________________________________________________
int CHardwareFacade::asyncSynchronizeFileSystem(CFileSystem *fs)
{
    int i;

    if (m_Simulation)
    {
        fs->SetAllFilesSynchronized (  );
        return 0;
    }

    if (fs == NULL)
        return -1;

    if (!serialPort->isOpen())
    {
        qDebug("SynchronizeFileSystem: Serial-port not open.\n");
        return -1;
    }

    // ============================================================================
    // Start transmitting all changed frames
    // ============================================================================
    for (i=0; i<fs->GetNumOfFiles (); i++)
    {
        file_list_it_t file = fs->GetFileFromIndex (i);

        if (!file->GetValidity())
        {
            file->SetDownloadingState(1);
            requestUpdateFileList();
            requestUpdateProgressBar(m_deviceNumber, 0);

            TransferFile (fs, file);

            file->SetDownloadingState(0);
            requestUpdateFileList();
        }
    }

    // ============================================================================
    // Finish by transmitting the entry table data
    // ============================================================================
    TransferEntryTable (fs);

    fs->SetAllFilesSynchronized (  );

    requestUpdateFileList();
    return 0;
}


//_________________________________________________________________________
int CHardwareFacade::CommandAndCheck ( const char *data )
{
    char dataTemp[1024];
    int readLength;
    int finished = 5;


    if (m_Simulation)
    {
        // return as if nothing bad has happenned
        return 0;
    }

    if (!serialPort->isOpen ())
    {
        qDebug("CommandAndCheck: Serial-port not open.\n");
        return -1;
    }

    while (finished--)
    {
        serialPort->write (data);
        qDebug("CommandAndCheck: sending data %s.\n", data);
        WAIT_FOR_BYTES_N(1000, 2);
        readLength = serialPort->read (dataTemp, 1024);    // empty buffer
        dataTemp[readLength] = '\0';

        StringToLower ( dataTemp );

        if (strstr(dataTemp, "ok") != NULL)
            return 0;
        if (strstr(dataTemp, "fail")!=NULL)
            return 1;

        if (dataTemp[0] == data[0] && dataTemp[1]==data[1])
        {
            msleep (50);
            readLength = serialPort->read (dataTemp, 1024);
            finished --;
        }
    }

    return -2;
}

//_________________________________________________________________________
int CHardwareFacade::SendDataCRC16 ( unsigned char *data, int length )
{
    char dataTemp[1024];
    int readLength;
    unsigned short CRC16 = 0xffff;
    int i;


    if (m_Simulation)
    {
        // return as if nothing bad has happenned
        return 0;
    }

    if (!serialPort->isOpen ())
    {
        qDebug("SendDataCRC16: Serial-port not open.\n");
        return -1;
    }

    qDebug ("Sending packet of length %d.\n", length);

    memcpy ( dataTemp, data, length );

    for (i = 0; i < length; i++)
        CRC16 = crc16_update(CRC16, data[i]);

    dataTemp [length]   = CRC16&0xff;
    dataTemp [length+1] = (CRC16>>8)&0xff;

    qDebug ("Appending CRC16 with LSB:%d, MSB:%d values.\n", dataTemp [length], dataTemp [length+1]);

    serialPort->write( dataTemp, length+2 );

    int timeOutTimer = 400;
    while (serialPort->bytesAvailable()<4 && timeOutTimer)
    {
        msleep(1);
        timeOutTimer--;
    }

    readLength = serialPort->read (dataTemp, 1024);    // empty buffer
    dataTemp[readLength] = '\0';

    qDebug ("Recieved: %s; Elapsed: %d\n", dataTemp, 200-timeOutTimer);

    StringToLower ( dataTemp );

    if (strstr(dataTemp, "okok") != NULL)
        return 0;

    if (strstr(dataTemp, "fail") != NULL)
        return 1;

    return -2;
}

//_________________________________________________________________________
int CHardwareFacade::WriteLedCalibrations ( int maxR, int maxG, int maxB )
{
    //QString retVal;
    //char data[256] = {0};
    //sprintf(data, "write_cal %d %d %d:", maxR, maxG, maxB);
    //return SendMessageAndWait (data, "OK", retVal, 60 );
    return 0;
}

//_________________________________________________________________________
int CHardwareFacade::ReadLedCalibrations  ( int *maxR, int *maxG, int *maxB )
{
    return 0;
}

//_________________________________________________________________________
int CHardwareFacade::asyncTestLeds (int rowNum, int delay, int turnOff)
{
    // if delay is -1 - light forever
    char data[256] = {0};
    sprintf(data, "test_led %d RGB %d:", rowNum, 8+rowNum);
    serialPort->write (data);
    WAIT_FOR_BYTES_N(20, 2);
    serialPort->read (data, 1024);    // empty buffer

    if (delay!=-1)
    {
        msleep (delay);
    }

    if (turnOff)
    {
        sprintf(data, "test_led %d RGB 0:", rowNum);
        serialPort->write (data);
        WAIT_FOR_BYTES_N(20, 2);
        serialPort->read (data, 1024);    // empty buffer
    }
    return 0;
}



//_________________________________________________________________________
//
//          W O R K E R   T H R E A D
//_________________________________________________________________________

//_________________________________________________________________________
void CHardwareFacade::onFinishedRunning ( void )
{
    qDebug ("Finished running a job.");
    m_working = false;
    disconnect(this, SIGNAL(finished()), this, SLOT(onFinishedRunning()));
}

//_________________________________________________________________________
int CHardwareFacade::TestLeds ( )
{
    if (m_working)
    {
        qDebug ("Still working...");
        return -1;
    }

    m_currentJob.enJobID = HADWARE_FACADE_JOB_TEST_LEDS;

    m_working = false;
    qDebug ("Starting thread.");
    start();

    qDebug ("Waiting for thread to start");
    while (!m_working)  msleep(50);

    qDebug ("Exiting SynchronizeFileSystem.");
    return 0;
}

//_________________________________________________________________________
int CHardwareFacade::SynchronizeFileSystem(CFileSystem *fs, QAfarsekFileList* fileList)
{
    if (m_working)
    {
        qDebug ("Still working...");
        return -1;
    }

    m_currentJob.fs = fs;
    m_currentJob.fileList = fileList;
    m_currentJob.enJobID = HADWARE_FACADE_JOB_SYNCRONIZE;

    m_working = false;
    qDebug ("Starting thread.");
    start();

    qDebug ("Waiting for thread to start");
    while (!m_working)  msleep(50);

    qDebug ("Exiting SynchronizeFileSystem.");
    return 0;
}


//_________________________________________________________________________
void CHardwareFacade::run ()
{
    int result = 0;
    int rowNum = 0;

    connect(this, SIGNAL(finished()), this, SLOT(onFinishedRunning()));

    if (m_currentJob.enJobID == HADWARE_FACADE_JOB_NO_JOB)
    {
        // nothing to do
        return;
    }

    m_working = true;

    switch (m_currentJob.enJobID)
    {
        case HADWARE_FACADE_JOB_TEST_LEDS:
            qDebug ("Starting HADWARE_FACADE_JOB_TEST_LEDS Job.");
            // sign by lighting the appropriate LEDs
            rowNum = 0;
            WriteLedCalibrations(255, 255, 255);

            for (rowNum = 0; rowNum < 8; rowNum ++)
            {
                if (rowNum == 7)
                    asyncTestLeds(rowNum%8, 100, 1);
                else asyncTestLeds(rowNum%8, 100, 0);
            }
            break;

        case HADWARE_FACADE_JOB_SYNCRONIZE:
            qDebug ("Starting HADWARE_FACADE_JOB_SYNCRONIZE with file_list job.");

            // connecting signals to slots
            connect(this,
                    SIGNAL(requestUpdateProgressBar(int, int)),
                    m_currentJob.fileList,
                    SLOT(do_UpdateProgressBar(int, int)));
            connect(this,
                    SIGNAL(requestUpdateFileList()),
                    m_currentJob.fileList,
                    SLOT(do_UpdateFileList()));

            result = asyncSynchronizeFileSystem(m_currentJob.fs);
            break;

        default:
            return;
            break;
    };
}

